package component;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;

import javax.swing.JLabel;

/**
 * @author http://www.java2s.com/Code/Java/Swing-JFC/Labelwithvariouseffects.htm
 * Class extending JLabel which can hold an outline to the text
 */
public class JLabel2D extends JLabel {
	private static final long serialVersionUID = 1L;
	
	public static final int EFFECT_PLAIN = 0;
	public static final int EFFECT_GRADIENT = 1;
	public static final int EFFECT_IMAGE = 2;
	public static final int EFFECT_IMAGE_ANIMATION = 3;
	public static final int EFFECT_COLOR_ANIMATION = 4;
	protected int effectIndex = EFFECT_PLAIN;
	protected double shearFactor = 0.0;
	protected Color outlineColor;
	protected Stroke stroke;
	protected GradientPaint gradient;
	protected Image foregroundImage;
	protected Thread animator;
	protected boolean isRunning = false;
	protected int m_delay;
	protected int m_xShift;

	public JLabel2D() {
		super();
	}

	public JLabel2D(String text) {
		super(text);
	}

	public JLabel2D(String text, int alignment) {
		super(text, alignment);
	}

	public void setEffectIndex(int e) {
		effectIndex = e;
		repaint();
	}

	public int getEffectIndex() {
		return effectIndex;
	}

	public void setShearFactor(double s) {
		shearFactor = s;
		repaint();
	}

	public double getShearFactor() {
		return shearFactor;
	}

	public void setOutlineColor(Color c) {
		outlineColor = c;
		repaint();
	}

	public Color getOutlineColor() {
		return outlineColor;
	}

	public void setStroke(Stroke s) {
		stroke = s;
		repaint();
	}

	public Stroke getStroke() {
		return stroke;
	}

	public void setGradient(GradientPaint g) {
		gradient = g;
		repaint();
	}

	public GradientPaint getGradient() {
		return gradient;
	}

	public void setForegroundImage(Image img) {
		foregroundImage = img;
		repaint();
	}

	public Image getForegroundImage() {
		return foregroundImage;
	}

	public void startAnimation(int delay) {
		if (animator != null)
			return;
		m_delay = delay;
		m_xShift = 0;
		isRunning = true;
		animator = new Thread() {
			double arg = 0;

			public void run() {
				while (isRunning) {
					if (effectIndex == EFFECT_IMAGE_ANIMATION)
						m_xShift += 10;
					else if (effectIndex == EFFECT_COLOR_ANIMATION && gradient != null) {
						arg += Math.PI / 10;
						double cos = Math.cos(arg);
						double f1 = (1 + cos) / 2;
						double f2 = (1 - cos) / 2;
						arg = arg % (Math.PI * 2);

						Color c1 = gradient.getColor1();
						Color c2 = gradient.getColor2();
						int r = (int) (c1.getRed() * f1 + c2.getRed() * f2);
						r = Math.min(Math.max(r, 0), 255);
						int g = (int) (c1.getGreen() * f1 + c2.getGreen() * f2);
						g = Math.min(Math.max(g, 0), 255);
						int b = (int) (c1.getBlue() * f1 + c2.getBlue() * f2);
						b = Math.min(Math.max(b, 0), 255);
						setForeground(new Color(r, g, b));
					}
					repaint();
					try {
						sleep(m_delay);
					} catch (InterruptedException ex) {
						break;
					}
				}
			}
		};
		animator.start();
	}

	public void stopAnimation() {
		isRunning = false;
		animator = null;
	}

	public void paintComponent(Graphics g) {
		Dimension d = getSize();
		Insets ins = getInsets();
		int x = ins.left;
		int y = ins.top;
		int w = d.width - ins.left - ins.right;
		int h = d.height - ins.top - ins.bottom;

		if (isOpaque()) {
			g.setColor(getBackground());
			g.fillRect(0, 0, d.width, d.height);
		}
		paintBorder(g);

		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

		FontRenderContext frc = g2.getFontRenderContext();
		TextLayout tl = new TextLayout(getText(), getFont(), frc);

		AffineTransform shear = AffineTransform.getShearInstance(shearFactor, 0.0);
		Shape src = tl.getOutline(shear);
		Rectangle rText = src.getBounds();

		float xText = x - rText.x;
		switch (getHorizontalAlignment()) {
		case CENTER:
			xText = x + (w - rText.width) / 2;
			break;
		case RIGHT:
			xText = x + (w - rText.width);
			break;
		}
		float yText = y + h / 2 + tl.getAscent() / 4;

		AffineTransform shift = AffineTransform.getTranslateInstance(xText, yText);
		Shape shp = shift.createTransformedShape(src);

		if (outlineColor != null) {
			g2.setColor(outlineColor);
			if (stroke != null)
				g2.setStroke(stroke);
			g2.draw(shp);
		}

		switch (effectIndex) {
		case EFFECT_GRADIENT:
			if (gradient == null)
				break;
			g2.setPaint(gradient);
			g2.fill(shp);
			break;

		case EFFECT_IMAGE:
			fillByImage(g2, shp, 0);
			break;

		case EFFECT_COLOR_ANIMATION:
			g2.setColor(getForeground());
			g2.fill(shp);
			break;

		case EFFECT_IMAGE_ANIMATION:
			if (foregroundImage == null)
				break;
			int wImg = foregroundImage.getWidth(this);
			if (m_xShift > wImg)
				m_xShift = 0;
			fillByImage(g2, shp, m_xShift - wImg);
			break;

		default:
			g2.setColor(getForeground());
			g2.fill(shp);
			break;
		}
	}

	protected void fillByImage(Graphics2D g2, Shape shape, int xOffset) {
		if (foregroundImage == null)
			return;
		int wImg = foregroundImage.getWidth(this);
		int hImg = foregroundImage.getHeight(this);
		if (wImg <= 0 || hImg <= 0)
			return;
		g2.setClip(shape);
		Rectangle bounds = shape.getBounds();
		for (int xx = bounds.x + xOffset; xx < bounds.x + bounds.width; xx += wImg)
			for (int yy = bounds.y; yy < bounds.y + bounds.height; yy += hImg)
				g2.drawImage(foregroundImage, xx, yy, this);
	}
}